import { createFunction, createFunctionLibrary, updateFunctionFiles, updateFunctionData, testFunction, updateFunctionLibrary } from '@/api/userProjectLibrary';
import { libraryFolderName, templateFolderName } from '@/config';
import { deleteObject, deleteObjectMulti, getFileList, getObjectUrl, putObject } from '@/utils/aliOss';
import { downloadOss } from '@/api/userProject';
import { getRandomLetter, getUuid } from '@/utils/util';
import Vue from 'vue';
import { Message } from 'element-ui';
import JSZip from 'jszip';
import user from './user';

const functions = {
  namespaced: true,
  state: {
    title: '函数库及函数',
    searching: false,
    showTreeData: [], // 展示节点
    defaultExpanded: [], // 展开节点
    editorFiles: [],
    templateFiles: {
      testing: {
        id: 'testing',
        path: '',
        label: 'testing.py',
        editContent: '',
        editText: ''
      },
      requirement: {
        id: 'requirement',
        path: '',
        label: 'requirements.txt',
        editContent: '',
        editText: ''
      },
      config: {
        id: 'config',
        path: '',
        label: 'config.yaml',
        editContent: '',
        editText: ''
      }
    },
    testcases: [],
    editorFilesId: '',
    // serviceName: sessionStorage.getItem('serviceName') || '',
    // functionName: sessionStorage.getItem('functionName') || '',
    serviceName: '',
    functionName: '',
    isUploadFile: {},
    isUploadTemplateFile: {},
    isUpdating: false,
    insertLines: 0,
    updateLog: '',
    showInstalledDialog: false,
    uploadNode: null,
    activeTab: 'first',
    usePublicLayers: ''
  },

  getters: {
    // TODO 复用这两个getters 可以较大简化代码
    /**
         * @param {Object}state
         * @returns {string} 用户函数目录
         * @constructor
         */
    libraryFunctionPath(state) {
      return `${libraryFolderName}/${state.serviceName}/${state.functionName}`;
    },
    templateFunctionPath(state) {
      return `${templateFolderName}/${state.serviceName}/${state.functionName}`;
    },
    // buckupFunctionPath(state) {
    //   return `backup/user.state.uid/${user.state.serviceName}_${state.functionName}_new Date().getTime()`;
    // },
    projectId(state) {
      return state.serviceName.substring(66) + '_' + state.functionName;
    },
    getDisplayLibraryName: (state, getters, rootState) => { // 去除用户前缀
      return state.serviceName.replace(rootState.user.userprefix, '');
    }
  },

  mutations: {
    // 从localstorage中读取信息
    SET_DATA: (state, payload) => {
      state.serviceName = payload.serviceName;
      state.functionName = payload.functionName;
    },
    // 设置页面标题
    setTitle: (state, payload) => {
      state.title = payload;
    },
    // 设置 testcases
    setTestcases: (state, payload) => {
      state.testcases = payload;
    },
    setSearching: (state, payload) => {
      state.searching = payload;
    },
    /**
     * 设置函数库名称和函数名
     * @param state
     * @param payload
     */
    setLibraryAndFunction: (state, payload) => {
      state.serviceName = payload.serviceName;
      state.functionName = payload.functionName;
      // sessionStorage.setItem('serviceName', state.serviceName);
      // sessionStorage.setItem('functionName', state.functionName);
    },
    // 设置树展开
    expand: (state, payload) => {
      state.defaultExpanded.push(payload);
    },
    // 设置树关闭
    collapse: (state, payload) => {
      state.defaultExpanded.remove(payload);
    },
    setTreeData: (state, payload) => {
      state.showTreeData = payload;
    },
    append: (state, payload) => {
      const { nodeType, type, data } = payload;
      const newChild = {
        isEditName: true,
        id: getRandomLetter() + getUuid(16), // 阿里云函数命名首位必须为字母
        label: '',
        isFirstBuild: true,
        hadRun: false
      };
      if (type === 'buildFolder' || type === 'buildRootFolder') { // 如果是文件夹，children为空，isFile为假
        newChild.children = [];
        newChild.isFile = false;
      } else { // 不是文件夹，isFile为真
        newChild.isFile = true;
      }
      // 对fatherId进行了赋值，原本的赋值大部分是无效的，只是对项目有作用
      if (nodeType === 'father') {
        newChild.path = '';
        newChild.fatherId = state.functionName;
        state.showTreeData.unshift(newChild);
        return;
      } else {
        newChild.fatherId = data.fatherId;
      }
      newChild.path = data.path ? `${data.path}/${data.label}` : '/' + data.label;
      newChild.isEditName = false;
      data.children.unshift(newChild);
      Vue.nextTick(() => {
        newChild.isEditName = true;
      });
    },
    // 删除节点
    remove: (state, payload) => {
      const { node, data } = payload;
      const parent = node.parent;
      const children = parent.data.children || parent.data;
      const index = children.findIndex((d) => d.id === data.id);
      children.splice(index, 1);
    },
    // 修改文件名
    editName: (state, payload) => {
      if (Object.keys(payload).includes('isEditName')) {
        payload.isEditName = true;
      } else {
        Vue.set(payload, 'isEditName', true);
      }
    },
    clearEditorFiles: (state) => {
      state.editorFiles = [];
      state.editorFilesId = '';
    },
    // 设置编辑器tab
    setEditorFiles: (state, payload) => {
      if (payload.type === 'add') {
        state.editorFiles.push(payload.data);
        state.editorFilesId = state.editorFiles[state.editorFiles.length - 1].id;
      } else if (payload.type === 'remove') {
        removeFromEditor(state, payload.data.id);
      }
    },
    // 设置编辑器当前显示tab
    setEditFileId: (state, payload) => {
      state.editorFilesId = payload;
    },
    setShowInstalledDialog: (state, val) => {
      state.showInstalledDialog = val;
    },
    /**
         * 设置template文件内容
         * @param state
         * @param {String} key
         * @param {String} content
         */
    setTemplateFileContent: (state, key, content) => {
      state.templateFiles[key].editContent = content;
      state.templateFiles[key].editText = content;
    },

    setUploadNode: (state, payload) => {
      state.uploadNode = payload;
    },

    setActiveTab: (state, tabName) => {
      state.activeTab = tabName;
    }
  },

  actions: {
    // 创建函数库
    createALiLibrary({ state }, payload) {
      return new Promise((resolve, reject) => {
        createFunctionLibrary(payload)
          .then(data => {
            resolve(data);
          })
          .catch(err => {
            reject(err);
          });
      });
    },
    // 更新函数库
    updateALiLibrary({ state }, payload) {
      return new Promise((resolve, reject) => {
        updateFunctionLibrary(payload)
          .then(data => {
            resolve(data);
          })
          .catch(err => {
            reject(err);
          });
      });
    },

    /**
     *
     * @param state
     * @param getters
     * @param fileData
     * @returns {Promise<void>}
     */
    buildTemplateFile: async({ state, getters }, fileData) => {
      console.log('调用更新文件的函数');
      state.isUploadTemplateFile[fileData.id] = true;
      const path = getters.templateFunctionPath;
      console.log('模板文件路径：', path);
      putObject(path, fileData.editContent || '', fileData.label)
        .then(() => {
          fileData.editText = fileData.editContent;
        }).catch((err) => {
          console.error(err);
        })
        .finally(_ => {
          state.isUploadTemplateFile[fileData.id] = false;
          Message({
            message: '保存成功',
            type: 'success'
          });
        });
    },
    // 创建测试事件
    buildTestcase: async({ state, getters }, testcase) => {
      if (testcase.name === '') {
        return;
      }
      // console.log('调用更新测试用例的函数');
      const path = getters.templateFunctionPath + '/testcase';
      const filename = testcase.name + '.json';
      putObject(path, testcase.content || '', filename)
        .then(() => {
        }).catch((err) => {
          console.error(err);
        })
        .finally(_ => {
          Message({
            message: '保存成功',
            type: 'success'
          });
        });
    },
    // 删除测试事件
    deleteFile: async({ state, getters }, testcase) => {
      // console.log('调用删除测试用例的函数');
      const path = getters.templateFunctionPath + '/testcase/' + testcase + '.json';
      deleteObject(path);
    },
    // 创建文件上传到阿里云
    buildFiles: async({ state, getters }, data) => {
      // 这个地方是对于进行了修改文件夹后的编辑器中路径的修改，其余的是一致的，所以只是更新路径即可
      const obj = state.editorFiles.find(item => {
        return item.id === data.id;
      });
      if (obj) {
        obj.path = data.path;
      }
      state.isUploadFile[data.id] = true;
      const file = state.editorFiles.find(item => (item.id === state.editorFilesId));
      const name2 = data.path ? data.path : '';
      // 如果path不为空，path='/test1'，就直接拼接
      // 如果path为空，就''
      const name = getters.libraryFunctionPath + name2;
      // putObject 参数： 路径，文件，文件名
      putObject(name, data.text || '', data.id + '.py')
        .then(() => {
          if (data.type === 'update') {
            file.editText = file.editContent;
          }
        })
        .catch((err) => {
          console.error(err);
        })
        .finally(_ => {
          state.isUploadFile[data.id] = false;
          Message({
            message: '保存成功',
            type: 'success'
          });
        });
    },
    /**
     * 创建函数
     * @param state
     * @param {Object} payload
     * @param {Object} payload.library
     * @param {Object} payload.functions
     * @param {string} payload.usePublicLayers
     * @param {String} payload.library.libraryName
     * @param {String} payload.functions.functionName
     * @param {String} payload.functions.timeOut
     * @param {String} payload.functions.memorySize
     * @returns {Promise<unknown>}
     */
    createAliFunction({ state }, payload) {
      return new Promise((resolve, reject) => {
        const { library, functions, usePublicLayers } = payload;
        functions.libraryName = library.libraryName;
        // TODO: function的初始filePathJson也可以放在后端
        functions.filePathJson = JSON.stringify([{
          id: functions.functionName,
          isFile: true,
          label: 'index.py',
          isEditName: false,
          isFirstBuild: false,
          children: [],
          fatherId: functions.functionName,
          path: ''
        }]);
        functions.publicLayer = usePublicLayers;
        createFunction(functions).then((response) => {
          resolve(response);
        }).catch((err) => {
          reject(err);
        });
      });
    },

    /**
     *
     * @param state
     * @param library
     * @param functionOption 可能没有这个字段
     * @param {String} functionOption.timeOut
     * @param {String} functionOption.memorySize
     *
     * @param usePublicLayers 可能没有这个字段
     * @param useFilePathJson 默认值设置为true
     * @return {Promise<unknown>}
     */
    updateAliFunctionFiles({ state }, { functionOption = {}, usePublicLayers = '' }) {
      state.isUpdating = true;
      return new Promise((resolve, reject) => {
        updateFunctionFiles({
          ...functionOption,
          libraryName: state.serviceName,
          functionName: state.functionName,
          usePublicLayers: usePublicLayers,
          filePathJson: JSON.stringify(state.showTreeData)
        }).then((res) => {
          resolve(res);
        }).catch((err) => {
          reject(err);
        }).finally(_ => {
          state.isUpdating = false;
        });
      });
    },
    // 获取文件

    // 文件改名，编辑器中的文件同时进行名称更新，但是其余的就不需要进行修改了
    handleEditorFileName: async({ state }, data) => {
      const obj = state.editorFiles.find(item => {
        return item.id === data.id;
      });
      if (obj) {
        obj.label = data.label;
      }
    },
    // 更新文件夹修改后的子节点路径
    updateChildPath: async({ dispatch }, data) => {
      const newPath = data.path ? `${data.path}/${data.label}` : `/${data.label}`;
      data.children.forEach(async(item) => {
        if (item.isFile === true) {
          item.text = '';
          await dispatch(`getOssFileText`, { item, newPath });
        }
        if (item.isFile === false) {
          item.path = newPath;
          await dispatch(`updateChildPath`, item);
        }
      });
    },
    // 获取Oss文件对应的内容，返还为text，便于写入新的文件中
    getOssFileText: async({ state, dispatch }, payload) => {
      const { item, newPath } = payload;
      const name1 = `${libraryFolderName}/${state.serviceName}/${state.functionName}`;
      const name2 = item.path ? `${item.path}/${item.id}.py` : `/${item.id}.py`;
      const name = name1 + name2;
      item.path = newPath;
      const url = await getObjectUrl(name);
      const resp = await downloadOss(url);
      item.text = await resp.text();
      dispatch(`buildFiles`, item);
      delete item.text;
    },
    /**
     * 获取Oss templateFiles
     * @param state
     * @param commit
     * @param getters
     */
    getTemplateFiles: async({ state, commit, getters }) => {
      // 获取 testing.py, dependency.txt, config.yaml
      for (const key in state.templateFiles) {
        const path = getters.templateFunctionPath + getOssFilePath(state.templateFiles[key]);
        const url = await getObjectUrl(path);
        const resp = await downloadOss(url);
        resp.text().then(function(content) {
          state.templateFiles[key].editContent = content;
          state.templateFiles[key].editText = content;
        });
      }
      console.log('获取模板文件完成');
      // 获取所有测试事件
      const testcasePath = getters.templateFunctionPath + '/testcase/';
      getFileList(testcasePath).then((result) => {
        const files = result.objects.filter((file) => file.name.endsWith('.json'));
        // 每次执行时要清空 testcases
        state.testcases = [];
        files.map((file) => {
          getObjectUrl(file.name).then((url) => {
            downloadOss(url).then((resp) => {
              resp.text().then(function(text) {
                const name = file.name.replace(testcasePath, '').replace('.json', '');
                const content = JSON.stringify(JSON.parse(text), undefined, 4);
                // console.log(content);
                state.testcases.push({ name, content });
              });
            });
          });
        });
      }).catch((error) => {
        console.error(error);
      });
      console.log('获取测试事件完成');
    },

    /**
         * 获取 libraryFolderName目录下文件 并添加到mainTab编辑器中
         * @param state
         * @param commit
         * @param getters
         * @param {Object} data
         * @param {String} data.id
         * @param {String} data.path
         * @returns {Promise<void>}
         */
    getFile: async({ state, commit, getters }, data) => {
      if (state.editorFiles.includeKey('id', data.id)) {
        state.editorFilesId = data.id;
        state.toolbarShowTab = 'editor';
        return;
      }
      console.log(data);
      // 如果是根目录文件,path='' TODO 有的地方用label作为目录 有的地方用id作为目录
      const name2 = data.path ? `${data.path}/${data.id}.py` : `/${data.id}.py`;
      const name = getters.libraryFunctionPath + name2;
      console.log(name);
      const url = await getObjectUrl(name);
      const resp = await downloadOss(url);
      const reader = new FileReader();
      reader.onload = function() {
        if (typeof reader.result === 'string') {
          commit('setEditorFiles', {
            type: 'add',
            data: {
              ...data,
              editContent: reader.result,
              editText: reader.result,
              display: 'file'
            }
          });
        }
      };
      reader.readAsText(resp);
    },
    append: async({ state }, data) => {
      const rc = state.showTreeData;
      await updateFunctionData({
        libraryName: state.serviceName,
        functionName: state.functionName,
        filePathJson: JSON.stringify(rc)
      });
    },

    // 删除项目
    remove: async({ state }, project) => {
      const rc = state.showTreeData;
      await updateFunctionData({ // 数据库中删除
        libraryName: state.serviceName,
        functionName: state.functionName,
        filePathJson: JSON.stringify(rc)
      });
      // 删除OSS上的所有文件
      deleteMutli(state, project);
    },

    // 备份要贡献的函数
    // contribute: async({ state }, payload) => {
    //   const zip = new JSZip();
    //   const { item, form } = payload;
    //   const info = `姓名: ${form.name}\n电话: ${form.phone}\n邮箱: ${form.email}`;
    //   const name = item.ossObjName;
    //   const url = await getObjectUrl(name);
    //   const resp = await downloadOss(url);
    //   const downFile = new File([resp], `${item.functionName}.zip`, { type: 'zip' });
    //   zip.file('info.txt', info);
    //   zip.file(`${item.functionName}.zip`, downFile);
    //   zip.generateAsync({
    //     type: 'blob',
    //     compression: 'DEFLATE'
    //   }).then((content) => {
    //     // const zipFileName = new Date().getTime() + '_' + user.state.uid + '_' + item.functionName;
    //     const zipFileName = item.functionName;
    //     const zipFile = new File([content], `${zipFileName}.zip`, { type: 'zip' });
    //     // return putObject('backup', zipFile);
    //     return putObject(`backup/${user.state.uid}/${new Date().getTime()}`, zipFile);
    //   });
    // },
    /**
         * 获取Oss templateFiles
         * @param state
         * @param commit
         * @param getters
         */
    contribute: async({ state, getters }, payload) => {
      const zip = new JSZip();
      const { item, form } = payload;
      const info = `姓名: ${form.name}\n电话: ${form.phone}\n邮箱: ${form.email}`;
      const name = item.ossObjName;
      const url = await getObjectUrl(name);
      const resp = await downloadOss(url);
      const downFile = new File([resp], `${item.functionName}.zip`, { type: 'zip' });

      // 下载三个模板文件
      const pyPath = getters.templateFunctionPath + getOssFilePath(state.templateFiles.testing);
      const pyUrl = await getObjectUrl(pyPath);
      const pyResp = await downloadOss(pyUrl);
      const pyFile = new File([pyResp], 'testing.py', { type: 'text/plain' });

      const txtPath = getters.templateFunctionPath + getOssFilePath(state.templateFiles.requirement);
      const txtUrl = await getObjectUrl(txtPath);
      const txtResp = await downloadOss(txtUrl);
      const txtFile = new File([txtResp], 'requirments.txt', { type: 'text/plain' });

      const yamlPath = getters.templateFunctionPath + getOssFilePath(state.templateFiles.config);
      const yamlUrl = await getObjectUrl(yamlPath);
      const yamlResp = await downloadOss(yamlUrl);
      const yamlFile = new File([yamlResp], 'config.yaml', { type: 'text/plain' });

      // 添加文件到zip
      zip.file('info.txt', info);
      zip.file(`${item.functionName}.zip`, downFile);
      zip.file('testing.py', pyFile);
      zip.file('requirments.txt', txtFile);
      zip.file('config.yaml', yamlFile);

      zip.generateAsync({
        type: 'blob',
        compression: 'DEFLATE'
      }).then((content) => {
        // const zipFileName = new Date().getTime() + '_' + user.state.uid + '_' + item.functionName;
        const zipFileName = item.functionName;
        const zipFile = new File([content], `${zipFileName}.zip`, { type: 'zip' });
        const backupPath = `backup/${user.state.uid}_${user.state.email}/${state.serviceName}/${state.functionName}/${new Date().getTime()}`;
        console.log(backupPath);
        return putObject(backupPath, zipFile);
      });
    },

    testing: async({ state }, payload) => {
      return new Promise((resolve, reject) => {
        // TODO: 翻译逻辑也在后端
        const { text, method } = payload;
        testFunction({
          serviceName: state.serviceName,
          functionName: state.functionName,
          data: text,
          method: method
        }).then((response) => {
          resolve(response);
        }).catch((err) => {
          reject(err);
        });
      });
    },
    // TODO: 上传文件放在后端做
    uploadFile: ({ state, dispatch }, file) => {
      return new Promise((resolve, reject) => {
        if (file) {
          const data = state.uploadNode;
          const isRoot = !data;
          const folder = isRoot ? '' : (data.path ? `${data.path}/${data.label}` : `/${data.label}`);
          const ossDir = `${libraryFolderName}/${state.serviceName}/${state.functionName}${folder}`;
          let child;
          if (isRoot) {
            child = state.showTreeData.find((item) => {
              return item.label === file.name;
            });
          } else {
            child = data.children.find((item) => {
              return item.label === file.name;
            });
          }
          if (child) {
            // 原来就已经存在的文件, 做覆盖就行
            putObject(ossDir, new File([file], child.id + '.py', { type: file.type })).then((res) => {
              resolve(res);
            }).catch((err) => {
              reject(err);
            });
          } else {
            const newChild = {
              isEditName: false,
              id: file.name,
              label: file.name,
              isFirstBuild: true,
              hadRun: false,
              isFile: true
            };
            if (isRoot) {
              newChild.path = '';
              newChild.fatherId = state.functionName;
              state.showTreeData.unshift(newChild);
            } else {
              newChild.fatherId = data.projectId || data.fatherId;
              newChild.path = data.path ? `${data.path}/${data.label}` : '/' + data.label;
              data.children.unshift(newChild);
            }
            putObject(ossDir, new File([file], newChild.id + '.py', { type: file.type })).then((res) => {
              dispatch('append').then(() => {
                resolve(res);
              }).catch((err) => {
                reject(err);
              });
            }).catch((err) => {
              reject(err);
            });
          }
        } else {
          reject('指定的上传文件数为0');
        }
      });
    }
  }
};

// 删除OSS上的文件
function deleteMutli(state, project) {
  if (project.isFile) {
    const name = `${libraryFolderName}/${state.serviceName}/${state.functionName}` +
      `${project.path}/${project.id}.py`;
    deleteObject(name);
    removeFromEditor(state, project.id);
    return;
  }
  const data = {
    path: [],
    projectPath: []
  };
  deleteProjectFiles(state, project.children, data);
  deleteObjectMulti(data.path);
}

// 递归删除OSS上的文件
function deleteProjectFiles(state, project, data) {
  project.map((item) => {
    if (item.isFile) {
      const name = `${libraryFolderName}/${state.serviceName}/${state.functionName}` +
        `${item.path}/${item.id}.py`;
      data.fileIds?.push(item.id);
      data.path?.push(name);
      data.files?.push(item);
      removeFromEditor(state, item.id);
    } else {
      if (item.children.length) {
        deleteProjectFiles(state, item.children, data);
      }
    }
  });
}

function removeFromEditor(state, id) {
  const index = state.editorFiles.findIndex((item) => {
    return item.id === id;
  });
  state.editorFiles.remove(function(item) {
    return item.id === id;
  });
  if (state.editorFiles.length === 0) {
    state.editorFilesId = '';
    state.toolbarShowTab = 'main';
  } else if (state.editorFilesId === id) {
    state.editorFilesId = index === 0 ? state.editorFiles[0].id : state.editorFiles[index - 1].id;
  }
}

/**
 * 计算得出Oss文件路径
 * @param data
 * @param {String} data.path
 * @param {String} data.label
 * @returns {string}
 */
function getOssFilePath(data) {
  return data.path ? `${data.path}/${data.label}` : `/${data.label}`;
}

export default functions;
